extends IBasicComponent


class_name InventoryComponent
"""
背包组件
"""


## 道具使能激发时触发
signal event_slot_triggered(inventory: InventoryComponent, slot: InventorySlot)


## 初始化时自带的道具
@export var items: Array[InventoryItem] = []

@export_category("Option")
## 容器大小
@export var inventory_size: int = 0: set = __set_slot_size
## 拾取时是否自动合并(PickUp时优先合并再使用空位,Place已存在道具则放置失败
@export var is_always_merged: bool = false
## 触发道具的鼠标按键
@export var equip_button: MouseButton = MOUSE_BUTTON_LEFT
## 空节点的样式
@export var slot_factory: INodeFactory = null
@export_category("")

@export_category("Auto")
## 未使用道具Container
@export var slot_container: Container = null
@export_category("")


## 所有道具槽位
var slots: Array[InventorySlot] = []


## 缓存空的槽位
var empty_slots: Array[InventorySlot] = []
## 缓存使用的槽位
var equip_slots: Array[InventorySlot] = []


func __check_exports() -> Dictionary:
    return IComponent.merge({
        "slot_factory": [IComponent.find_child.bind(self, func(x): return x is INodeFactory, slot_factory)],
        "slot_container": IComponent.find_down.bind(self, func(x): return x is Container, slot_container),
    }, super.__check_exports())

func __prepare() -> void:
    super.__prepare()

    var count: int = 0
    for slot in slot_container.get_children():
        if slot is InventorySlot:
            slot.gui_input.connect(__on_slot_gui_input.bind(slot))
            slot.event_item_will_changed.connect(__on_slot_item_will_changed)
            slot.name = "Slot[%03d]" % (count + 1)
            count += 1
            slots.append(slot)
            if slot.is_empty():
                empty_slots.append(slot)
            else:
                equip_slots.append(slot)
    __set_slot_size(inventory_size)

    for item in items:
        if not place(item):
            break

func __set_slot_size(value: int) -> void:
    var slot_count: int = len(slots)
    if slot_count >= value:
        return
    if slot_factory:
        for i in range(value - slot_count):
            var slot: InventorySlot = slot_factory.create() as InventorySlot
            slot.name = "Slot[%03d]" % (slot_count + i)
            if slot_factory.mount_node == null:
                NodeHelper.add_child(slot_container, slot)
            slot.gui_input.connect(__on_slot_gui_input.bind(slot))
            slot.event_item_will_changed.connect(__on_slot_item_will_changed)
            slots.append(slot)
            empty_slots.append(slot)
    inventory_size = value

func __on_slot_gui_input(event: InputEvent, slot: InventorySlot) -> void:
    if not slot.is_empty() and NodeHelper.is_mouse_pressed(event, equip_button):
        event_slot_triggered.emit(self, slot)
        slot.accept_event()

func __sort_slot(a: InventorySlot, b: InventorySlot) -> int:
    return a.name.naturalnocasecmp_to(b.name) < 0

func __on_slot_item_will_changed(slot: InventorySlot, item: InventoryItem) ->void:
    if slot.inventory_item == null and item:
        equip_slots.append(slot)
        equip_slots.sort_custom(__sort_slot)
        empty_slots.erase(slot)
    elif slot.inventory_item and item == null:
        empty_slots.append(slot)
        empty_slots.sort_custom(__sort_slot)
        equip_slots.erase(slot)

# 放置道具到一个空的槽位(不校验数量),返回是否成功放置
func place(item: InventoryItem) -> bool:
    if len(empty_slots) < 1:
        return false
    if is_always_merged:
        for slot in equip_slots:
            if slot.inventory_item.source == item.source:
                return false
    var slot: InventorySlot = empty_slots[0]
    slot.set_item(item)
    return true

# 拾取道具,返回是否全部拾取(剩余部分仍然在item中)
func pickup(item: InventoryItem) -> bool:
    var first_empty_slot: InventorySlot = null
    var left: InventoryItem = item
    # 尝试在已使用的Slot中合并
    if is_always_merged:
        for slot in equip_slots:
            # 类型不匹配
            if not slot.is_equal(item):
                continue
            # 合并
            left = slot.place(left)
            # 已经合并完成
            if left == null:
                return true
    # 在空的Slot中处理剩余的容量
    for slot in empty_slots.duplicate():
        # 按照最大堆叠数量分批放置到后续的Slot中
        if item.amount > item.source.max_stack_count:
            slot.set_item(item.clip(item.source.max_stack_count))
        else:
            slot.set_item(item)
            return true
    # 最后剩余放不下的道具
    return false

# 丢弃道具
func discard(item: InventoryItem) -> void:
    for slot in equip_slots:
        if slot.is_equal(item):
            pass
